#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>


#define BUFSIZE 1024
#define PKGLEN 500

typedef int SOCK;
typedef struct sockaddr_in SOCKADDR;

void responde(SOCK conn);

int main() {

    SOCK server, conn;
    SOCKADDR servAddr, connAddr;

    if((server = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        printf("create socket error(%d): %s\n", errno, strerror(errno));
        exit(0);
    }

    memset(&servAddr, 0, sizeof(servAddr));
    servAddr.sin_family = AF_INET;
    servAddr.sin_addr.s_addr = INADDR_ANY;
    servAddr.sin_port = htons(7288);

    if(bind(server, (struct sockaddr*)&servAddr, sizeof(servAddr)) == -1) {
        printf("bind socket error(%d): %s\n", errno, strerror(errno));
        exit(0);
    }

    if(listen(server, 10) == -1) {
        printf("socket listen error(%d): %s\n", errno, strerror(errno));
        exit(0);
    }

    printf("监听中......\n");

    int addrLen = sizeof(connAddr);
    char addrBuf[32] = {0};
    while(1) {
        if((conn = accept(server, (struct sockaddr*)&connAddr, &addrLen)) == -1) {
            printf("accept connection error(%d): %s\n", errno, strerror(errno));
            continue;
        }
        if(inet_ntop(AF_INET, &connAddr.sin_addr, addrBuf, sizeof(addrBuf)) == NULL) {
            printf("net address errro(%d), %s\n", errno, strerror(errno));
            exit(0);
        }
        printf("接收到连接请求: %s!\n连接成功!\n", addrBuf);
        responde(conn);
    }

    close(server);

    return 0;
}

void recvFile(SOCK conn, char *fileName, int fileLen);
void sendFileList(SOCK conn);

void sendFile(SOCK conn, char *fileName);
void sendFileNotExist(SOCK conn);
void sendFileLen(SOCK conn, int fileLen);
void sendFileContent(SOCK conn, FILE *file, int fileLen);

void responde(SOCK conn) {
    char recvBuf[BUFSIZE];
    int recvLen;
    int closed = 0;

    while(1) {
        if(closed) break;

        if((recvLen = recv(conn, recvBuf, sizeof(recvBuf), 0)) == -1) {
            printf("recv errro(%d): %s\n", errno, strerror(errno));
            exit(0);
        }
        switch (recvBuf[0])
        {
            case '0':
                close(conn);
                closed = 1;
                printf("本次连接关闭!等待下个连接\n");
                break;
            case '1':
                printf("接受文件: %s 文件大小: %d字节\n", recvBuf + 1, *(int *)(recvBuf + 33));
                recvFile(conn, recvBuf + 1, *(int *)(recvBuf + 33));
                break;
            case '2':
                printf("收到下载请求!\n");
                sendFileList(conn);
                break;
            case '4':
                sendFile(conn, recvBuf + 1);
                break;
            default:
                break;
        }
    }
}

void sendFileNotExist(SOCK conn) {
    char sendBuf[BUFSIZ] = {0};
    sendBuf[0] = '4';
    *(int *)(sendBuf + 1) = 0;
    if(send(conn, sendBuf, 5, 0) == -1) {
        printf("send error(%d): %s\n", errno, strerror(errno));
        exit(0);
    }
}
void sendFileLen(SOCK conn, int fileLen) {
    char sendBuf[BUFSIZE] = {0};
    sendBuf[0] = '4';
    *(int *)(sendBuf + 1) = fileLen;
    if(send(conn, sendBuf, 5, 0) == -1) {
        printf("send error(%d): %s\n", errno, strerror(errno));
    }
}
void sendFileContent(SOCK conn, FILE *file, int fileLen) {
    char sendBuf[BUFSIZE], ackBuf[8];
    int bytesSent = 0, sendLen, sentLen;
    while(bytesSent < fileLen) {
        memset(sendBuf, 0, sizeof(sendBuf));
        sendBuf[0] = '5';

        if(bytesSent + PKGLEN > fileLen) sendLen = fileLen - bytesSent;
        else sendLen = PKGLEN;

        *(int *)(sendBuf + 1) = sendLen;
        fread(sendBuf + 5, sizeof(char), sendLen, file);

        if((sentLen = send(conn, sendBuf, sendLen + 5, 0)) == -1) {
            printf("send file content error(%d): %d\n", errno, strerror(errno));
            exit(0);
        }
        // printf("%s %d %s %d\n", sendBuf, *(int*)(sendBuf + 1), sendBuf + 5, sendLen);
        if(recv(conn, ackBuf, sizeof(ackBuf), 0) <= 0) {
            printf("recv error(%d): %s\n", errno, strerror(errno));
            exit(0);
        }
        bytesSent += sendLen;
    }
}
void sendFile(SOCK conn, char *fileName) {
    FILE *file;
    if((file = fopen(fileName, "r")) == NULL) {
        printf("open file: %s error(%d): %s\n", fileName, errno, strerror(errno));
        sendFileNotExist(conn);
        return ;
    }
    int fileLen;
    fseek(file, 0, SEEK_END);
    fileLen = ftell(file) - 1;
    fseek(file, 0, SEEK_SET);
    printf("传输中...\n");

    sendFileLen(conn, fileLen);

    char recvBuf[BUFSIZE] = {0};
    recv(conn, recvBuf, sizeof(recvBuf), 0);

    if(recvBuf[0] == '9') {
        // printf("client ready to recv\n");
        sendFileContent(conn, file, fileLen);
    }

    fclose(file);
    printf("传输完成!\n");

}


void sendFileList(SOCK conn) {
    char sendBuf[BUFSIZE] = {0};
    DIR* dir;
    struct dirent *ent;
    if((dir = opendir(".")) == NULL) {
        printf("open dir error(%d): %s\n");
        exit(0);
    }
    sendBuf[0] = '3';
    int fileCount = 0;
    while((ent = readdir(dir)) != NULL) {
        if(ent->d_type == 8) {
            strncpy(sendBuf + 5 + (fileCount++) * 32, ent->d_name, strlen(ent->d_name) + 1);
        }
    }
    *(int *)(sendBuf + 1) = fileCount;
    if(send(conn, sendBuf, 5 + 32 * fileCount, 0) == -1) {
        printf("send error(%d): %s\n", errno, strerror(errno));
        exit(0);
    }
}

void recvFile(SOCK conn, char *fileName, int fileLen) {
    char sendBuf[BUFSIZE] = {0}, ackBuf[] = "7";
    sendBuf[0] = '1';
    send(conn, sendBuf, 1, 0);

    char recvBuf[BUFSIZE] = {0};

    // printf("%s, %d\n", fileName, fileLen);

    FILE *file;
    if((file = fopen(fileName, "w")) == NULL) {
        printf("create file %s error(%d): %s\n", errno, strerror(errno));
        exit(0);
    }

    printf("接收中...\n");
    int recvLen = 0, bytesRecv = 0;
    while(bytesRecv < fileLen) {
        if((recvLen = recv(conn, recvBuf, sizeof(recvBuf), 0)) <= 0) {
            printf("recv error(%d): %s\n", errno, strerror(errno));
            exit(0);
        }
        if(recvBuf[0] != '5') {
            printf("unknown error\n");
            exit(0);
        }
        if(send(conn, ackBuf, 1, 0) == -1) {
            printf("ack error(%d): %s\n", errno, strerror(errno));
            exit(0);
        }
        recvLen = *(int *)(recvBuf + 1);
        fwrite(recvBuf + 5, sizeof(char), recvLen, file);
        bytesRecv += recvLen;
    }
    fflush(file);
    fclose(file);
    printf("接收完成!\n");
}
